// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/cache_storage/cache_storage_dispatcher.h"

#include <stddef.h>

#include <map>
#include <memory>
#include <string>
#include <utility>

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_local.h"
#include "content/child/thread_safe_sender.h"
#include "content/common/cache_storage/cache_storage_messages.h"
#include "content/public/common/referrer.h"
#include "content/public/renderer/render_thread.h"
#include "content/renderer/service_worker/service_worker_type_util.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerCache.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerRequest.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerResponse.h"
#include "url/origin.h"

using base::TimeTicks;

namespace content {

using blink::WebServiceWorkerCacheError;
using blink::WebServiceWorkerCacheStorage;
using blink::WebServiceWorkerRequest;
using blink::WebString;

static base::LazyInstance<base::ThreadLocalPointer<CacheStorageDispatcher>>::
    Leaky g_cache_storage_dispatcher_tls
    = LAZY_INSTANCE_INITIALIZER;

namespace {

    CacheStorageDispatcher* const kHasBeenDeleted = reinterpret_cast<CacheStorageDispatcher*>(0x1);

    ServiceWorkerFetchRequest FetchRequestFromWebRequest(
        const blink::WebServiceWorkerRequest& web_request)
    {
        ServiceWorkerHeaderMap headers;
        GetServiceWorkerHeaderMapFromWebRequest(web_request, &headers);

        return ServiceWorkerFetchRequest(
            web_request.url(), web_request.method().ascii(), headers,
            Referrer(web_request.referrerUrl(), web_request.getReferrerPolicy()),
            web_request.isReload());
    }

    void PopulateWebRequestFromFetchRequest(
        const ServiceWorkerFetchRequest& request,
        blink::WebServiceWorkerRequest* web_request)
    {
        web_request->setURL(request.url);
        web_request->setMethod(WebString::fromASCII(request.method));
        for (ServiceWorkerHeaderMap::const_iterator i = request.headers.begin(),
                                                    end = request.headers.end();
             i != end; ++i) {
            web_request->setHeader(WebString::fromASCII(i->first),
                WebString::fromASCII(i->second));
        }
        web_request->setReferrer(WebString::fromASCII(request.referrer.url.spec()),
            request.referrer.policy);
        web_request->setIsReload(request.is_reload);
    }

    blink::WebVector<blink::WebServiceWorkerRequest> WebRequestsFromRequests(
        const std::vector<ServiceWorkerFetchRequest>& requests)
    {
        blink::WebVector<blink::WebServiceWorkerRequest> web_requests(
            requests.size());
        for (size_t i = 0; i < requests.size(); ++i)
            PopulateWebRequestFromFetchRequest(requests[i], &(web_requests[i]));
        return web_requests;
    }

    CacheStorageCacheQueryParams QueryParamsFromWebQueryParams(
        const blink::WebServiceWorkerCache::QueryParams& web_query_params)
    {
        CacheStorageCacheQueryParams query_params;
        query_params.ignore_search = web_query_params.ignoreSearch;
        query_params.ignore_method = web_query_params.ignoreMethod;
        query_params.ignore_vary = web_query_params.ignoreVary;
        query_params.cache_name = blink::WebString::toNullableString16(web_query_params.cacheName);
        return query_params;
    }

    CacheStorageCacheOperationType CacheOperationTypeFromWebCacheOperationType(
        blink::WebServiceWorkerCache::OperationType operation_type)
    {
        switch (operation_type) {
        case blink::WebServiceWorkerCache::OperationTypePut:
            return CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT;
        case blink::WebServiceWorkerCache::OperationTypeDelete:
            return CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE;
        default:
            return CACHE_STORAGE_CACHE_OPERATION_TYPE_UNDEFINED;
        }
    }

    CacheStorageBatchOperation BatchOperationFromWebBatchOperation(
        const blink::WebServiceWorkerCache::BatchOperation& web_operation)
    {
        // We don't support streaming for cache.
        DCHECK(web_operation.response.streamURL().isEmpty());

        CacheStorageBatchOperation operation;
        operation.operation_type = CacheOperationTypeFromWebCacheOperationType(web_operation.operationType);
        operation.request = FetchRequestFromWebRequest(web_operation.request);
        operation.response = GetServiceWorkerResponseFromWebResponse(web_operation.response);
        operation.match_params = QueryParamsFromWebQueryParams(web_operation.matchParams);
        return operation;
    }

    template <typename T>
    void ClearCallbacksMapWithErrors(T* callbacks_map)
    {
        typename T::iterator iter(callbacks_map);
        while (!iter.IsAtEnd()) {
            iter.GetCurrentValue()->onError(blink::WebServiceWorkerCacheErrorNotFound);
            callbacks_map->Remove(iter.GetCurrentKey());
            iter.Advance();
        }
    }

} // namespace

// The WebCache object is the Chromium side implementation of the Blink
// WebServiceWorkerCache API. Most of its methods delegate directly to the
// ServiceWorkerStorage object, which is able to assign unique IDs as well
// as have a lifetime longer than the requests.
class CacheStorageDispatcher::WebCache : public blink::WebServiceWorkerCache {
public:
    WebCache(base::WeakPtr<CacheStorageDispatcher> dispatcher, int cache_id)
        : dispatcher_(dispatcher)
        , cache_id_(cache_id)
    {
    }

    ~WebCache() override
    {
        if (dispatcher_)
            dispatcher_->OnWebCacheDestruction(cache_id_);
    }

    // From blink::WebServiceWorkerCache:
    void dispatchMatch(std::unique_ptr<CacheMatchCallbacks> callbacks,
        const blink::WebServiceWorkerRequest& request,
        const QueryParams& query_params) override
    {
        if (!dispatcher_)
            return;
        dispatcher_->dispatchMatchForCache(cache_id_, std::move(callbacks), request,
            query_params);
    }
    void dispatchMatchAll(std::unique_ptr<CacheWithResponsesCallbacks> callbacks,
        const blink::WebServiceWorkerRequest& request,
        const QueryParams& query_params) override
    {
        if (!dispatcher_)
            return;
        dispatcher_->dispatchMatchAllForCache(cache_id_, std::move(callbacks),
            request, query_params);
    }
    void dispatchKeys(std::unique_ptr<CacheWithRequestsCallbacks> callbacks,
        const blink::WebServiceWorkerRequest& request,
        const QueryParams& query_params) override
    {
        if (!dispatcher_)
            return;
        dispatcher_->dispatchKeysForCache(cache_id_, std::move(callbacks), request,
            query_params);
    }
    void dispatchBatch(
        std::unique_ptr<CacheBatchCallbacks> callbacks,
        const blink::WebVector<BatchOperation>& batch_operations) override
    {
        if (!dispatcher_)
            return;
        dispatcher_->dispatchBatchForCache(cache_id_, std::move(callbacks),
            batch_operations);
    }

private:
    const base::WeakPtr<CacheStorageDispatcher> dispatcher_;
    const int cache_id_;
};

CacheStorageDispatcher::CacheStorageDispatcher(
    ThreadSafeSender* thread_safe_sender)
    : thread_safe_sender_(thread_safe_sender)
    , weak_factory_(this)
{
    g_cache_storage_dispatcher_tls.Pointer()->Set(this);
}

CacheStorageDispatcher::~CacheStorageDispatcher()
{
    ClearCallbacksMapWithErrors(&has_callbacks_);
    ClearCallbacksMapWithErrors(&open_callbacks_);
    ClearCallbacksMapWithErrors(&delete_callbacks_);
    ClearCallbacksMapWithErrors(&keys_callbacks_);
    ClearCallbacksMapWithErrors(&match_callbacks_);

    ClearCallbacksMapWithErrors(&cache_match_callbacks_);
    ClearCallbacksMapWithErrors(&cache_match_all_callbacks_);
    ClearCallbacksMapWithErrors(&cache_keys_callbacks_);
    ClearCallbacksMapWithErrors(&cache_batch_callbacks_);

    g_cache_storage_dispatcher_tls.Pointer()->Set(kHasBeenDeleted);
}

CacheStorageDispatcher* CacheStorageDispatcher::ThreadSpecificInstance(
    ThreadSafeSender* thread_safe_sender)
{
    if (g_cache_storage_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted) {
        NOTREACHED() << "Re-instantiating TLS CacheStorageDispatcher.";
        g_cache_storage_dispatcher_tls.Pointer()->Set(NULL);
    }
    if (g_cache_storage_dispatcher_tls.Pointer()->Get())
        return g_cache_storage_dispatcher_tls.Pointer()->Get();

    CacheStorageDispatcher* dispatcher = new CacheStorageDispatcher(thread_safe_sender);
    if (WorkerThread::GetCurrentId())
        WorkerThread::AddObserver(dispatcher);
    return dispatcher;
}

void CacheStorageDispatcher::WillStopCurrentWorkerThread()
{
    delete this;
}

bool CacheStorageDispatcher::Send(IPC::Message* msg)
{
    return thread_safe_sender_->Send(msg);
}

bool CacheStorageDispatcher::OnMessageReceived(const IPC::Message& message)
{
    bool handled = true;
    IPC_BEGIN_MESSAGE_MAP(CacheStorageDispatcher, message)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheStorageHasSuccess,
        OnCacheStorageHasSuccess)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheStorageOpenSuccess,
        OnCacheStorageOpenSuccess)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheStorageDeleteSuccess,
        OnCacheStorageDeleteSuccess)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheStorageKeysSuccess,
        OnCacheStorageKeysSuccess)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheStorageMatchSuccess,
        OnCacheStorageMatchSuccess)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheStorageHasError,
        OnCacheStorageHasError)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheStorageOpenError,
        OnCacheStorageOpenError)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheStorageDeleteError,
        OnCacheStorageDeleteError)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheStorageMatchError,
        OnCacheStorageMatchError)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheMatchSuccess,
        OnCacheMatchSuccess)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheMatchAllSuccess,
        OnCacheMatchAllSuccess)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheKeysSuccess, OnCacheKeysSuccess)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheBatchSuccess,
        OnCacheBatchSuccess)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheMatchError, OnCacheMatchError)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheMatchAllError,
        OnCacheMatchAllError)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheKeysError, OnCacheKeysError)
    IPC_MESSAGE_HANDLER(CacheStorageMsg_CacheBatchError, OnCacheBatchError)
    IPC_MESSAGE_UNHANDLED(handled = false)
    IPC_END_MESSAGE_MAP()

    return handled;
}

void CacheStorageDispatcher::OnCacheStorageHasSuccess(int thread_id,
    int request_id)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    UMA_HISTOGRAM_TIMES("ServiceWorkerCache.CacheStorage.Has",
        TimeTicks::Now() - has_times_[request_id]);
    WebServiceWorkerCacheStorage::CacheStorageCallbacks* callbacks = has_callbacks_.Lookup(request_id);
    callbacks->onSuccess();
    has_callbacks_.Remove(request_id);
    has_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheStorageOpenSuccess(int thread_id,
    int request_id,
    int cache_id)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    std::unique_ptr<WebCache> web_cache(
        new WebCache(weak_factory_.GetWeakPtr(), cache_id));
    web_caches_.AddWithID(web_cache.get(), cache_id);
    UMA_HISTOGRAM_TIMES("ServiceWorkerCache.CacheStorage.Open",
        TimeTicks::Now() - open_times_[request_id]);
    WebServiceWorkerCacheStorage::CacheStorageWithCacheCallbacks* callbacks = open_callbacks_.Lookup(request_id);
    callbacks->onSuccess(std::move(web_cache));
    open_callbacks_.Remove(request_id);
    open_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheStorageDeleteSuccess(int thread_id,
    int request_id)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    UMA_HISTOGRAM_TIMES("ServiceWorkerCache.CacheStorage.Delete",
        TimeTicks::Now() - delete_times_[request_id]);
    WebServiceWorkerCacheStorage::CacheStorageCallbacks* callbacks = delete_callbacks_.Lookup(request_id);
    callbacks->onSuccess();
    delete_callbacks_.Remove(request_id);
    delete_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheStorageKeysSuccess(
    int thread_id,
    int request_id,
    const std::vector<base::string16>& keys)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    blink::WebVector<blink::WebString> web_keys(keys.size());
    std::transform(
        keys.begin(), keys.end(), web_keys.begin(),
        [](const base::string16& s) { return WebString::fromUTF16(s); });
    UMA_HISTOGRAM_TIMES("ServiceWorkerCache.CacheStorage.Keys",
        TimeTicks::Now() - keys_times_[request_id]);
    WebServiceWorkerCacheStorage::CacheStorageKeysCallbacks* callbacks = keys_callbacks_.Lookup(request_id);
    callbacks->onSuccess(web_keys);
    keys_callbacks_.Remove(request_id);
    keys_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheStorageMatchSuccess(
    int thread_id,
    int request_id,
    const ServiceWorkerResponse& response)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    blink::WebServiceWorkerResponse web_response;
    PopulateWebResponseFromResponse(response, &web_response);

    UMA_HISTOGRAM_TIMES("ServiceWorkerCache.CacheStorage.Match",
        TimeTicks::Now() - match_times_[request_id]);
    WebServiceWorkerCacheStorage::CacheStorageMatchCallbacks* callbacks = match_callbacks_.Lookup(request_id);
    callbacks->onSuccess(web_response);
    match_callbacks_.Remove(request_id);
    match_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheStorageHasError(
    int thread_id,
    int request_id,
    blink::WebServiceWorkerCacheError reason)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    WebServiceWorkerCacheStorage::CacheStorageCallbacks* callbacks = has_callbacks_.Lookup(request_id);
    callbacks->onError(reason);
    has_callbacks_.Remove(request_id);
    has_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheStorageOpenError(
    int thread_id,
    int request_id,
    blink::WebServiceWorkerCacheError reason)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    WebServiceWorkerCacheStorage::CacheStorageWithCacheCallbacks* callbacks = open_callbacks_.Lookup(request_id);
    callbacks->onError(reason);
    open_callbacks_.Remove(request_id);
    open_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheStorageDeleteError(
    int thread_id,
    int request_id,
    blink::WebServiceWorkerCacheError reason)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    WebServiceWorkerCacheStorage::CacheStorageCallbacks* callbacks = delete_callbacks_.Lookup(request_id);
    callbacks->onError(reason);
    delete_callbacks_.Remove(request_id);
    delete_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheStorageMatchError(
    int thread_id,
    int request_id,
    blink::WebServiceWorkerCacheError reason)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    WebServiceWorkerCacheStorage::CacheStorageMatchCallbacks* callbacks = match_callbacks_.Lookup(request_id);
    callbacks->onError(reason);
    match_callbacks_.Remove(request_id);
    match_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheMatchSuccess(
    int thread_id,
    int request_id,
    const ServiceWorkerResponse& response)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    blink::WebServiceWorkerResponse web_response;
    PopulateWebResponseFromResponse(response, &web_response);

    UMA_HISTOGRAM_TIMES("ServiceWorkerCache.Cache.Match",
        TimeTicks::Now() - cache_match_times_[request_id]);
    blink::WebServiceWorkerCache::CacheMatchCallbacks* callbacks = cache_match_callbacks_.Lookup(request_id);
    callbacks->onSuccess(web_response);
    cache_match_callbacks_.Remove(request_id);
    cache_match_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheMatchAllSuccess(
    int thread_id,
    int request_id,
    const std::vector<ServiceWorkerResponse>& responses)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());

    UMA_HISTOGRAM_TIMES("ServiceWorkerCache.Cache.MatchAll",
        TimeTicks::Now() - cache_match_all_times_[request_id]);
    blink::WebServiceWorkerCache::CacheWithResponsesCallbacks* callbacks = cache_match_all_callbacks_.Lookup(request_id);
    callbacks->onSuccess(WebResponsesFromResponses(responses));
    cache_match_all_callbacks_.Remove(request_id);
    cache_match_all_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheKeysSuccess(
    int thread_id,
    int request_id,
    const std::vector<ServiceWorkerFetchRequest>& requests)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());

    UMA_HISTOGRAM_TIMES("ServiceWorkerCache.Cache.Keys",
        TimeTicks::Now() - cache_keys_times_[request_id]);
    blink::WebServiceWorkerCache::CacheWithRequestsCallbacks* callbacks = cache_keys_callbacks_.Lookup(request_id);
    callbacks->onSuccess(WebRequestsFromRequests(requests));
    cache_keys_callbacks_.Remove(request_id);
    cache_keys_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheBatchSuccess(
    int thread_id,
    int request_id)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());

    UMA_HISTOGRAM_TIMES("ServiceWorkerCache.Cache.Batch",
        TimeTicks::Now() - cache_batch_times_[request_id]);
    blink::WebServiceWorkerCache::CacheBatchCallbacks* callbacks = cache_batch_callbacks_.Lookup(request_id);
    callbacks->onSuccess();
    cache_batch_callbacks_.Remove(request_id);
    cache_batch_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheMatchError(
    int thread_id,
    int request_id,
    blink::WebServiceWorkerCacheError reason)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    blink::WebServiceWorkerCache::CacheMatchCallbacks* callbacks = cache_match_callbacks_.Lookup(request_id);
    callbacks->onError(reason);
    cache_match_callbacks_.Remove(request_id);
    cache_match_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheMatchAllError(
    int thread_id,
    int request_id,
    blink::WebServiceWorkerCacheError reason)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    blink::WebServiceWorkerCache::CacheWithResponsesCallbacks* callbacks = cache_match_all_callbacks_.Lookup(request_id);
    callbacks->onError(reason);
    cache_match_all_callbacks_.Remove(request_id);
    cache_match_all_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheKeysError(
    int thread_id,
    int request_id,
    blink::WebServiceWorkerCacheError reason)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    blink::WebServiceWorkerCache::CacheWithRequestsCallbacks* callbacks = cache_keys_callbacks_.Lookup(request_id);
    callbacks->onError(reason);
    cache_keys_callbacks_.Remove(request_id);
    cache_keys_times_.erase(request_id);
}

void CacheStorageDispatcher::OnCacheBatchError(
    int thread_id,
    int request_id,
    blink::WebServiceWorkerCacheError reason)
{
    DCHECK_EQ(thread_id, CurrentWorkerId());
    blink::WebServiceWorkerCache::CacheBatchCallbacks* callbacks = cache_batch_callbacks_.Lookup(request_id);
    callbacks->onError(blink::WebServiceWorkerCacheError(reason));
    cache_batch_callbacks_.Remove(request_id);
    cache_batch_times_.erase(request_id);
}

void CacheStorageDispatcher::dispatchHas(
    std::unique_ptr<WebServiceWorkerCacheStorage::CacheStorageCallbacks>
        callbacks,
    const url::Origin& origin,
    const blink::WebString& cacheName)
{
    int request_id = has_callbacks_.Add(std::move(callbacks));
    has_times_[request_id] = base::TimeTicks::Now();
    Send(new CacheStorageHostMsg_CacheStorageHas(CurrentWorkerId(), request_id,
        origin, cacheName.utf16()));
}

void CacheStorageDispatcher::dispatchOpen(
    std::unique_ptr<
        WebServiceWorkerCacheStorage::CacheStorageWithCacheCallbacks>
        callbacks,
    const url::Origin& origin,
    const blink::WebString& cacheName)
{
    int request_id = open_callbacks_.Add(std::move(callbacks));
    open_times_[request_id] = base::TimeTicks::Now();
    Send(new CacheStorageHostMsg_CacheStorageOpen(CurrentWorkerId(), request_id,
        origin, cacheName.utf16()));
}

void CacheStorageDispatcher::dispatchDelete(
    std::unique_ptr<WebServiceWorkerCacheStorage::CacheStorageCallbacks>
        callbacks,
    const url::Origin& origin,
    const blink::WebString& cacheName)
{
    int request_id = delete_callbacks_.Add(std::move(callbacks));
    delete_times_[request_id] = base::TimeTicks::Now();
    Send(new CacheStorageHostMsg_CacheStorageDelete(CurrentWorkerId(), request_id,
        origin, cacheName.utf16()));
}

void CacheStorageDispatcher::dispatchKeys(
    std::unique_ptr<WebServiceWorkerCacheStorage::CacheStorageKeysCallbacks>
        callbacks,
    const url::Origin& origin)
{
    int request_id = keys_callbacks_.Add(std::move(callbacks));
    keys_times_[request_id] = base::TimeTicks::Now();
    Send(new CacheStorageHostMsg_CacheStorageKeys(CurrentWorkerId(), request_id,
        origin));
}

void CacheStorageDispatcher::dispatchMatch(
    std::unique_ptr<WebServiceWorkerCacheStorage::CacheStorageMatchCallbacks>
        callbacks,
    const url::Origin& origin,
    const blink::WebServiceWorkerRequest& request,
    const blink::WebServiceWorkerCache::QueryParams& query_params)
{
    int request_id = match_callbacks_.Add(std::move(callbacks));
    match_times_[request_id] = base::TimeTicks::Now();
    Send(new CacheStorageHostMsg_CacheStorageMatch(
        CurrentWorkerId(), request_id, origin,
        FetchRequestFromWebRequest(request),
        QueryParamsFromWebQueryParams(query_params)));
}

void CacheStorageDispatcher::dispatchMatchForCache(
    int cache_id,
    std::unique_ptr<blink::WebServiceWorkerCache::CacheMatchCallbacks>
        callbacks,
    const blink::WebServiceWorkerRequest& request,
    const blink::WebServiceWorkerCache::QueryParams& query_params)
{
    int request_id = cache_match_callbacks_.Add(std::move(callbacks));
    cache_match_times_[request_id] = base::TimeTicks::Now();

    Send(new CacheStorageHostMsg_CacheMatch(
        CurrentWorkerId(), request_id, cache_id,
        FetchRequestFromWebRequest(request),
        QueryParamsFromWebQueryParams(query_params)));
}

void CacheStorageDispatcher::dispatchMatchAllForCache(
    int cache_id,
    std::unique_ptr<blink::WebServiceWorkerCache::CacheWithResponsesCallbacks>
        callbacks,
    const blink::WebServiceWorkerRequest& request,
    const blink::WebServiceWorkerCache::QueryParams& query_params)
{
    int request_id = cache_match_all_callbacks_.Add(std::move(callbacks));
    cache_match_all_times_[request_id] = base::TimeTicks::Now();

    Send(new CacheStorageHostMsg_CacheMatchAll(
        CurrentWorkerId(), request_id, cache_id,
        FetchRequestFromWebRequest(request),
        QueryParamsFromWebQueryParams(query_params)));
}

void CacheStorageDispatcher::dispatchKeysForCache(
    int cache_id,
    std::unique_ptr<blink::WebServiceWorkerCache::CacheWithRequestsCallbacks>
        callbacks,
    const blink::WebServiceWorkerRequest& request,
    const blink::WebServiceWorkerCache::QueryParams& query_params)
{
    int request_id = cache_keys_callbacks_.Add(std::move(callbacks));
    cache_keys_times_[request_id] = base::TimeTicks::Now();

    Send(new CacheStorageHostMsg_CacheKeys(
        CurrentWorkerId(), request_id, cache_id,
        FetchRequestFromWebRequest(request),
        QueryParamsFromWebQueryParams(query_params)));
}

void CacheStorageDispatcher::dispatchBatchForCache(
    int cache_id,
    std::unique_ptr<blink::WebServiceWorkerCache::CacheBatchCallbacks>
        callbacks,
    const blink::WebVector<blink::WebServiceWorkerCache::BatchOperation>&
        web_operations)
{
    int request_id = cache_batch_callbacks_.Add(std::move(callbacks));
    cache_batch_times_[request_id] = base::TimeTicks::Now();

    std::vector<CacheStorageBatchOperation> operations;
    operations.reserve(web_operations.size());
    for (size_t i = 0; i < web_operations.size(); ++i) {
        operations.push_back(
            BatchOperationFromWebBatchOperation(web_operations[i]));
    }

    Send(new CacheStorageHostMsg_CacheBatch(CurrentWorkerId(), request_id,
        cache_id, operations));
}

void CacheStorageDispatcher::OnWebCacheDestruction(int cache_id)
{
    web_caches_.Remove(cache_id);
    Send(new CacheStorageHostMsg_CacheClosed(cache_id));
}

void CacheStorageDispatcher::PopulateWebResponseFromResponse(
    const ServiceWorkerResponse& response,
    blink::WebServiceWorkerResponse* web_response)
{
    web_response->setURLList(response.url_list);
    web_response->setStatus(response.status_code);
    web_response->setStatusText(WebString::fromASCII(response.status_text));
    web_response->setResponseType(response.response_type);
    web_response->setResponseTime(response.response_time.ToInternalValue());
    web_response->setCacheStorageCacheName(
        response.is_in_cache_storage
            ? blink::WebString::fromUTF8(response.cache_storage_cache_name)
            : blink::WebString());
    blink::WebVector<blink::WebString> headers(
        response.cors_exposed_header_names.size());
    std::transform(response.cors_exposed_header_names.begin(),
        response.cors_exposed_header_names.end(), headers.begin(),
        [](const std::string& s) { return WebString::fromLatin1(s); });
    web_response->setCorsExposedHeaderNames(headers);

    for (const auto& i : response.headers) {
        web_response->setHeader(WebString::fromASCII(i.first),
            WebString::fromASCII(i.second));
    }

    if (!response.blob_uuid.empty()) {
        web_response->setBlob(blink::WebString::fromUTF8(response.blob_uuid),
            response.blob_size);
        // Let the host know that it can release its reference to the blob.
        Send(new CacheStorageHostMsg_BlobDataHandled(response.blob_uuid));
    }
}

blink::WebVector<blink::WebServiceWorkerResponse>
CacheStorageDispatcher::WebResponsesFromResponses(
    const std::vector<ServiceWorkerResponse>& responses)
{
    blink::WebVector<blink::WebServiceWorkerResponse> web_responses(
        responses.size());
    for (size_t i = 0; i < responses.size(); ++i)
        PopulateWebResponseFromResponse(responses[i], &(web_responses[i]));
    return web_responses;
}

} // namespace content
